﻿using System;
using System.Collections.Generic;
using System.IO;
using CSScriptLibrary;

namespace Chimp.Core
{
    public static class JointSettings
    {
        public static List<string> ImportPaths = new List<string>();
        public static void RegPath(string p)
        {
       
            if(ImportPaths.Contains(p)) ImportPaths.Remove(p);
            ImportPaths.Add(p);
        }
        public static string LoadImport(string p)
        {
            p = p + ".joint";
            foreach(var ip in ImportPaths)
            {
                string ap = ip + "\\" + p;
                if(File.Exists(ap))
                {
                    return File.ReadAllText(ap);
                }
            }
            Console.WriteLine("Failed to load import:" + p + " in any registered path.");
            return "";
        }
    }
    public class JMember
    {
        public string Name { get; set; }
        public Dictionary<string, string> Tags = new Dictionary<string, string> ( );
        public List<JMember> Members = new List<JMember> ( );
        public bool HasTag ( string t )
        {
            if ( Tags.ContainsKey ( t ) ) return true;
            return false;
        }
      
        public JMember AddMember ( string id, params string[ ] tags )
        {
            var m = new JMember ( ) { Name = id };
            foreach ( var t in tags )
            {
                m.Tags.Add ( t, t );
            }
            Members.Add ( m );
            return m;
        }
        public List<JMember> FilterMembers ( params string[ ] tag )
        {
            var ml = new List<JMember> ( );
            foreach ( var m in Members )
            {
                bool match = true;
                foreach ( var t in tag )
                {
                    if ( !m.HasTag ( t ) )
                    {
                        match = false;
                    }
                }
                if ( match )
                {
                    ml.Add ( m );
                }
            }
            return ml;
        }
    }
        public class JointReflection
        {
            public JMember Root = new JMember ( );
            public JMember GetTypeFor(JMember v)
            {
                if ( v.Tags.ContainsKey ( "Type" ) )
                {
                    var ty = v.Tags["Type"];
                    foreach(var m in Root.Members)
                    {
                        if(ty==m.Name)
                        {
                            return m;
                        }
                    }
                }
                return null;
            }
            public JointReflection ( JointApp app )
            {
                foreach ( var s in app.Apps.Keys )
                {
                    var st = app.Apps[ s ];
                    var c = Root.AddMember ( s, "Class", "State" );
                    foreach ( var v in st.Vars.Keys )
                    {
                        var vt = st.Vars[ v ];
                        var vm = c.AddMember ( vt.Name, "Var", vt.CurType, vt.IsStatic == true ? "Static" : "Instance" );
                        vm.Tags.Add("Type",vt.CurType);
                    }
                    foreach ( var e in st.Nodes.Keys )
                    {
                        var et = st.Nodes[ e ];
                        c.AddMember ( et.Name, "Event", "Method", "Instance" );
                    }
                }
            }

        }
        public class JointUI : JointNode
        {
            public class UIPart : JointNode
            {
                public List<UIPart> Parts = new List<UIPart> ( );
                public UIPart ( JointNode p )
                {
                    SetData ( p );
                }
                public override void ParseNode ( )
                {
                    Type = GetTok ( );
                    Name = GetTok ( );
                    X = new Expr ( this );
                    Y = new Expr ( this );
                    W = new Expr ( this );
                    H = new Expr ( this );
                    if ( PeekTok ( ) == "(" )
                    {
                        Text = new Expr ( this );
                    }
                    while ( !Eof )
                    {
                        var ct = PeekTok ( );
                        if ( ct == "End" )
                        {
                            return;
                        }
                        Parts.Add ( new UIPart ( this ) );
                    }
                }
                public override void WriteOutput ( )
                {
                    w ( Type + " " + Name + " = new " + Type + "(" );
                    X.WriteOutput ( );
                    w ( "," );
                    Y.WriteOutput ( );
                    w ( "," );
                    W.WriteOutput ( );
                    w ( "," );
                    H.WriteOutput ( );
                    w ( "," );
                    Text.WriteOutput ( );
                    if ( Parent.GetType ( ) == this.GetType ( ) )
                    {
                        w ( "," + Parent.Name + ");\n\n" );
                    }
                    else
                    {
                        w ( ",null);\n\n" );
                    }

                    foreach ( var p in Parts )
                    {
                        p.WriteOutput ( );
                    }
                }
                public string Type { get; set; }
                public Expr X, Y, W, H;
                public Expr Text { get; set; }
            }
            public JointUI ( JointNode p )
            {
                SetData ( p );
            }
            public override void ParseNode ( )
            {
                Name = GetTok ( );
                Root = new UIPart ( this );
            }
            public override void WriteOutput ( )
            {
                l ( "\npublic class " + Name + " : UIDoc\n{\n" );
                l ( "\npublic override ChimpWidget CreateUI()\n{\n" );
                Root.WriteOutput ( );
                l ( "\nreturn " + Root.Name + ";\n" );
                l ( "\n}\n" );
                l ( "\n}\n" );
            }


            public UIPart Root = null;
        }
        public class JointScreenFilter : JointNode
        {


            public JointScreenFilter ( JointNode p )
            {
                SetData ( p );
            }

            public List<string> TexList = new List<string> ( );
            public List<Var> ParList = new List<Var> ( );
            public List<Var> WorkList = new List<Var> ( );
            public string CodeList = "";
            public Dictionary<string, bool> ScratchMap = new Dictionary<string, bool> ( );
            public override void WriteOutput ( )
            {
                System.IO.File.WriteAllText ( Name + ".glsl", CodeList );
                l ( "public class " + Name + " : ChimpPostFX\n{\n" );
                l ( "public " + Name + "()\n{\n Effect = new ChimpEffect(null,\"" + Name + ".glsl\");\n" );
                foreach ( var p in TexList )
                {
                    var s = ScratchMap[ p ];
                    if ( s )
                    {
                        l ( p + " = new ChimpTexture2D((int)ChimpApp.W,(int)ChimpApp.H);" );
                        l ( p + "RT = new ChimpRenderTarget();\n" );

                    }
                }
                l ( "\n}\n" );
                foreach ( var t in TexList )
                {
                    l ( "\npublic ChimpTexture2D " + t + " = null;\n" );
                    l ( "\npublic ChimpRenderTarget " + t + "RT = null;\n" );
                    l ( "\npublic void Use" + t + "()\n{\n" );
                    l ( "\n   " + t + "RT.Bind();" );
                    l ( "\n   " + t + "RT.AttachTexture(" + t + ");" );
                    l ( "\nGL.Clear(ClearBufferMask.ColorBufferBit);" );
                    l ( "\n}\n" );
                    l ( "\npublic void Stop" + t + "()\n{\n" );
                    l ( "\n   " + t + "RT.Unbind();" );
                    l ( "\n}\n" );
                }
                foreach ( var p in ParList )
                {
                    var ut = "";
                    switch ( p.CurType.ToLower ( ) )
                    {
                        case "vec4":
                            ut = "Vector4";
                            break;
                        case "vec3":
                            ut = "Vector3";
                            break;
                        case "pixel":
                            ut = "Vector4";
                            break;
                        case "float":
                            ut = "float";
                            break;
                        case "int":
                            ut = "int";
                            break;
                    }
                    l ( "public " + ut + " " + p.Name + ";" );

                }
                l ( "public override void OnSetPars()\n{\n" );
                l ( " Effect.SetInt(\"SourceMap\",0);" );
                int i2 = 1;
                foreach ( var t2 in TexList )
                {


                    l ( "Effect.SetInt(\"" + t2 + "\"," + i2 + ");\n" );

                    i2++;
                }

                foreach ( var p in ParList )
                {
                    switch ( p.CurType.ToLower ( ) )
                    {
                        case "vec4":
                        case "pixel":
                            l ( "Effect.SetVec4(\"" + p.Name + "\"," + p.Name + ");\n" );
                            break;
                        case "vec3":
                            l ( "Effect.SetVec3(\"" + p.Name + "\"," + p.Name + ");\n" );
                            break;
                        case "float":
                            l ( "Effect.SetFloat(\"" + p.Name + "\"," + p.Name + ");\n" );
                            break;
                        case "int":
                            l ( "Effect.SetVec4(\"" + p.Name + "\"," + p.Name + ");\n" );
                            break;
                    }
                }
                l ( "\n}\n" );
                foreach ( var n in Nodes.Keys )
                {
                    var an = Nodes[ n ];

                    an.WriteOutput ( );
                }
                l ( "\n}\n" );

            }
            public override void ParseNode ( )
            {
                Name = GetTok ( );
                CodeList = "";
                while ( !Eof )
                {

                    var t = GetTok ( );

                    switch ( t )
                    {
                        case "Event":
                            SetNode ( PeekTok ( ), new JointBasic ( GetTok ( ), this ) { Stub = false } );
                            break;
                        case "Shader":
                            CodeList += "\nuniform sampler2D SourceMap;";
                            foreach ( var tex in TexList )
                            {
                                CodeList += "\nuniform sampler2D " + tex + ";\n\n";
                            }
                            foreach ( var pa in ParList )
                            {
                                CodeList += "\nuniform " + pa.CurType.ToLower ( ) + " " + pa.Name + ";\n\n";
                            }
                            CodeList += "\n\nvec4 readPixel(sampler2D tex,float u,float v)\n{\nvec2 uv;\nuv.x=u;\nuv.y=v;\nreturn texture2D(tex,uv);\n}\n\n";
                            CodeList += "void main()\n{\n";
                            foreach ( var wa in WorkList )
                            {
                                CodeList += "\n" + wa.CurType.ToLower ( ) + " " + wa.Name + ";\n\n";
                            }
                            int ec = 1;
                            while ( !Eof )
                            {

                                var st = GetTok ( );
                                Console.WriteLine ( "ST=" + st );
                                bool done = false;
                                switch ( st )
                                {

                                    case "SetPixel":

                                        var pix = GetTok ( ).ToLower ( );
                                        if ( pix == "destpixel" )
                                        {
                                            pix = "gl_FragColor";
                                        }
                                        if ( PeekTok ( ) == "To" )
                                        {
                                            GetTok ( );
                                        }
                                        string e = "";
                                        ReadExpr ( ref e );
                                        CodeList += "\n" + pix + " = " + e + ";\n\n";
                                        break;
                                    case "Loop":
                                        var fv = "";
                                        var tv = "";
                                        var van = GetTok ( ).ToLower ( );
                                        ReadExpr ( ref fv );
                                        if ( PeekTok ( ) == "To" )
                                        {
                                            GetTok ( );
                                        }
                                        ReadExpr ( ref tv );
                                        CodeList += "for(" + van + "=" + fv + ";" + van + "<" + tv + ";" + van + "++)\n{\n";
                                        ec++;
                                        Console.WriteLine ( "For=" + van + " " + fv + " To " + tv );
                                        break;
                                    case "End":
                                        CodeList += "\n}\n";
                                        ec--;
                                        if ( ec == 0 )
                                        {
                                            done = true;
                                            break;
                                        }
                                        break;
                                    default:
                                        string a = " ";
                                        string b = " ";
                                        if ( ltok == "." ) a = "";
                                        if ( PeekTok ( ) == "." ) b = "";
                                        if ( st == "." )
                                        {
                                            a = "";
                                            b = "";
                                        }
                                        switch ( st )
                                        {
                                            case "PixelAX":
                                                st = "gl_TexCoord[0].x";
                                                break;
                                            case "PixelAY":
                                                st = "gl_TexCoord[0].y";
                                                break;
                                            case "DestPixel":
                                                st = "gl_FragColor";
                                                break;
                                        }
                                        CodeList += a + st + b;
                                        if ( st == ";" )
                                        {
                                            CodeList += "\n\n";
                                        }
                                        ltok = st;
                                        break;
                                }
                                if ( done ) break;
                            }

                            break;
                        case "Tex":

                            GetTok ( );
                            while ( !Eof )
                            {
                                TexList.Add ( GetTok ( ) );
                                if ( PeekTok ( ) == "Work" )
                                {
                                    Console.WriteLine ( "Set To Work:" + TexList[ TexList.Count - 1 ] );
                                    GetTok ( );
                                    ScratchMap.Add ( TexList[ TexList.Count - 1 ], true );
                                }
                                else
                                {
                                    ScratchMap.Add ( TexList[ TexList.Count - 1 ], false );
                                }
                                if ( PeekTok ( ) == ")" )
                                {
                                    GetTok ( );
                                    break;
                                }
                                if ( PeekTok ( ) == "," )
                                {
                                    GetTok ( );
                                }
                            }


                            break;
                        case "Par":
                            GetTok ( );
                            while ( !Eof )
                            {
                                var vt = GetTok ( );
                                vt = vt.Replace ( "Pixel", "vec4" );
                                var vn = GetTok ( ).ToLower ( );
                                ParList.Add ( new Var ( vn, vt ) );
                                Console.WriteLine ( "Par:" + vn + " T:" + vt );
                                if ( PeekTok ( ) == ")" )
                                {
                                    GetTok ( );
                                    break;
                                }
                                if ( PeekTok ( ) == "," )
                                {
                                    GetTok ( );
                                }

                            }
                            break;
                        case "Var":
                            GetTok ( );
                            while ( !Eof )
                            {
                                var vt = GetTok ( );
                                vt = vt.Replace ( "Pixel", "vec4" );
                                var vn = GetTok ( );
                                Console.WriteLine ( "Var:" + vn + " T:" + vt );
                                WorkList.Add ( new Var ( vn.ToLower ( ), vt ) );
                                if ( PeekTok ( ) == ")" )
                                {
                                    GetTok ( );
                                    break;
                                }
                                if ( PeekTok ( ) == "," )
                                {
                                    GetTok ( );
                                }

                            }
                            break;
                        case "End":
                            return;
                            break;
                    }

                }
            }

            private string ltok = "";
            private void ReadExpr ( ref string fv )
            {

                GetTok ( );
                int ec = 1;
                fv = "";
                while ( ec > 0 )
                {
                    if ( PeekTok ( ) == "(" )
                    {
                        ec++;
                    }
                    if ( PeekTok ( ) == "PixelAX" )
                    {
                        fv += "gl_TexCoord[0].x";
                    }
                    else if ( PeekTok ( ) == "PixelAY" )
                    {
                        fv += "gl_TexCoord[0].y";

                    }
                    else
                    {
                        if ( TexList.Contains ( PeekTok ( ) ) || PeekTok ( ) == "SourceMap" )
                        {
                            fv += "readPixel(" + PeekTok ( ) + ",";
                            string e1 = "";
                            GetTok ( );
                            if ( PeekTok ( ) == "(" )
                            {
                                GetTok ( );
                            }
                            ReadExpr ( ref e1 );
                            fv += e1;
                            e1 = "";
                            if ( PeekTok ( ) == "," )
                            {
                                GetTok ( );
                            }
                            fv += ",";
                            ReadExpr ( ref e1 );
                            fv += e1 + ")";
                            if ( PeekTok ( ) == ")" )
                            {
                                GetTok ( );
                            }
                        }
                        else
                        {
                            fv += PeekTok ( ).ToLower ( );
                        }
                    }
                    if ( GetTok ( ) == ")" )
                    {
                        // fv += ")";
                        ec--;
                    }
                }
                ec = 0;
                var nv = "";
                for ( int ii=0; ii < fv.Length; ii++ )
                {
                    if ( fv[ ii ].ToString ( ) == "(" )
                    {
                        ec++;
                    }
                    if ( fv[ ii ].ToString ( ) == ")" )
                    {
                        ec--;

                        if ( ec >= 0 )
                        {
                            nv += fv[ ii ].ToString ( );
                        }
                    }
                    else
                    {

                        nv += fv[ ii ].ToString ( );
                    }
                }
                fv = nv;
            }
        }
        public class JointApp : JointNode
        {
            public Dictionary<string, AppState> Apps = new Dictionary<string, AppState> ( );
            private Chimp.Core.AppState AutoState;
            public ChimpState CurState { get; set; }
            public class HostApp : ChimpApp
            {
                public string AutoState { get; set; }
                public HostApp ( string auto, int w, int h, string title, bool full )
                    : base ( w, h, title, full )
                {
                    AutoState = auto;
                }
                //   public override void DoInit ( )
                // {
                //ChimpApp.PushState(AutoState);
                //}
            }

            public HostApp Host = null;
            public virtual void Play ( params object[ ] pars )
            {

                if ( AutoState == null )
                {

                    Console.WriteLine ( "No state was declared Auto. This must be done first." );
                
                }
                AH = new AsmHelper ( CSScript.Load ( CSPath ) );
                CurState = ( ChimpState )AH.TryCreateObject ( AutoState.Name + "." + AutoState.Name );
                RegisterStuff obj = AH.TryCreateObject ( AutoState.Name + "." + "RegisterClasses" ) as RegisterStuff;
                obj.RegAll ( );
                //  Host = new HostApp(AutoState.Name, 800,600,"You are smoking a SpaceChimp Joint!",false);

                //ChimpApp.PushState(AutoState.Name);
                Console.WriteLine ( "About to smoke joint." );
                //Host.Run();
                Console.WriteLine ( "Smoked." );



            }
            public JointApp(string code,string tmp) : base(code,tmp)
            {
               
            }
            public JointApp ( string path )
                : base ( path )
            {
            }

            public List<AppTransient> TList = new List<AppTransient> ( );
            public string AppName { get; set; }
            public string CSPath = "";
            public override void CreateOutput ( )
            {
                Console.WriteLine ( "created:" );

                while ( true )
                {
                    CSPath = new Random ( Environment.TickCount ).Next ( 0, 5000000 ) + "_" +
                             new Random ( Environment.TickCount ).Next ( 0, 5000000 ) + ".cs";
                    if ( System.IO.File.Exists ( CSPath ) == false ) break;
                }

                _cf = new FileStream ( CSPath, FileMode.Create, FileAccess.Write );
                _cw = new StreamWriter ( _cf );
            }

            public override void CloseOutput ( )
            {
                _cf.Flush ( );
                _cw.Flush ( );
                _cw = null;
                _cf.Close ( );
                _cf = null;
                Console.WriteLine ( "Closed:" );
            }

            public override void WriteOutput ( )
            {
                WriteCSharpHeader ( );
                l ( "\npublic class RegisterClasses : RegisterStuff\n{\n" );
                l ( "\n   public override void RegAll()\n{\n" );
                foreach ( var a in Apps.Keys )
                {
                    var ap = Apps[ a ];
                    l ( "\n ChimpApp.RegState(\"" + ap.Name + "\",new " + ap.Name + "());" );

                }
                foreach ( var ui in IList )
                {
                    l ( "\nUIDoc.RegDoc(\"" + ui.Name + "\",new " + ui.Name + "());\n\n" );
                }
                l ( "\n ChimpApp.PushState(\"" + AutoState.Name + "\");" );
                l ( "\n}\n}\n" );
                foreach ( var i in IList )
                {
                    i.WriteOutput ( );
                }
                foreach ( var f in FList )
                {
                    f.WriteOutput ( );
                }
                foreach ( var t in TList )
                {
                    t.WriteOutput ( );
                }
                WriteNodes ( );
                WriteCSharpFooter ( );
            }


            public void WriteCSharpHeader ( )
            {
                Console.WriteLine ( "Wrote" );
                _cw.WriteLine ( "using System;" );
                _cw.WriteLine ( "using System.IO;" );
                _cw.WriteLine ( "using System.Collections.Generic;" );
                _cw.WriteLine ( "using Chimp.Core;" );
                _cw.WriteLine ( "using Chimp.Core.Actors;" );
                _cw.WriteLine ( "using Chimp.Core.PostFX;" );
                l ( "using OpenTK;" );
                l ( "using OpenTK.Graphics.OpenGL;" );
                _cw.WriteLine ( "namespace " + AppName + "\n{" );
            }

            public void WriteCSharpFooter ( )
            {
                _cw.WriteLine ( "}" );
            }

            public List<JointUI> IList = new List<JointUI> ( );
            public List<JointScreenFilter> FList = new List<JointScreenFilter> ( );
            public override void ParseNode ( )
            {
                while ( !Eof )
                {
                    //Console.WriteLine("Parsing Begun...");
                    string t = GetTok ( );
                    //Console.WriteLine("Tok:" + t);
                    switch ( t )
                    {
                        case "Import":
                            if(PeekTok()=="(")
                            {
                                GetTok();
                            }else
                            {
                                
                                Console.WriteLine("Import format is 'Import (Lib)'");
                                return;
                            }
                            string p = "";
                            while(!Eof)
                            {
                                bool done = false;
                                var pp = GetTok();
                                switch(pp)
                                {
                                    case ")":
                                        done = true;
                                        break;
                                    case ".":
                                        p += "\\";
                                        break;
                                    default:
                                        p += pp;
                                        break;
                                }
                                if ( done ) break;
                            }
                            var code = JointSettings.LoadImport(p);
                            _file.Insert(_i, code);
                            Console.WriteLine("Imported:" + p.Replace("\\", "."));
                            Console.WriteLine("ImportedCode:\n"+code);
                            break;

                        case "UI":
                            IList.Add ( new JointUI ( this ) );
                            break;
                        case "ScreenFilter":
                            FList.Add ( new JointScreenFilter ( this ) );
                            break;
                        case "Transient":

                            var ts1 = new AppTransient ( this );
                            TList.Add ( ts1 );

                            break;
                        case "State":

                            Console.WriteLine ( "Parsing App" );
                            string name = GetTok ( );
                            AppName = name;
                            var app = new AppState ( name, this );
                            if ( app.Auto )
                            {
                                AutoState = app;
                            }
                            Apps.Add ( name, app );
                            app.Name = name;
                            SetNode ( name, app );

                            break;
                    }
                }
            }
        }
    }
